namespace SimpleWebServices.Web.Mvc
{
    using System;
    using System.IO;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    using Moq;
    using SimpleWebServices.IO;
    using Xunit;

    public sealed class AllowAttributeFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<AllowAttribute>()
                .DerivesFrom<ActionFilterAttribute>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .IsDecoratedWith<AttributeUsageAttribute>()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new AllowAttribute());
        }

        [Fact]
        public void prop_Methods()
        {
            Assert.True(new PropertyExpectations(typeof(AllowAttribute).GetProperty("Methods"))
                .IsAutoProperty<string>("GET, HEAD, OPTIONS")
                .Set("GET, HEAD, OPTIONS, POST")
                .Result);
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContextNull()
        {
            Assert.Throws<ArgumentNullException>(() => new AllowAttribute().OnActionExecuting(null));
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContext_whenMethodAllowed()
        {
            var mock = new Mock<HttpContextBase>();
            mock.SetupGet<string>(x => x.Request.HttpMethod).Returns("GET").Verifiable();

            AllowAttribute obj = new AllowAttribute
            {
                Methods = "GET, HEAD, OPTIONS"
            };

            obj.OnActionExecuting(new ActionExecutingContext { HttpContext = mock.Object });

            mock.VerifyAll();
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContext_whenMethodNotAllowed()
        {
            using (MemoryStream stream = new MemoryStream())
            {
                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.HttpMethod).Returns("PUT").Verifiable();
                mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
                mock.Setup(x => x.Response.Cache.SetExpires(It.IsAny<DateTime>())).Verifiable();
                mock.Setup(x => x.Response.AppendHeader("Allow", "GET, HEAD, OPTIONS")).Verifiable();
                mock.Setup(x => x.Response.Clear()).Verifiable();
                mock.Setup(x => x.Response.Filter).Returns(new WrappedStream(stream)).Verifiable();
                mock.SetupSet(x => x.Response.Filter).Callback(actual => Assert.Same(stream, actual)).Verifiable();
                mock.SetupSet(x => x.Response.StatusCode).Callback(actual => Assert.Equal<int>(actual, (int)HttpStatusCode.MethodNotAllowed)).Verifiable();
                mock.Setup(x => x.Response.End()).Verifiable();

                AllowAttribute obj = new AllowAttribute
                {
                    Methods = "GET, HEAD, OPTIONS"
                };

                obj.OnActionExecuting(new ActionExecutingContext { HttpContext = mock.Object });

                mock.VerifyAll();
            }
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContext_whenMethods()
        {
            var mock = new Mock<HttpContextBase>();
            mock.SetupGet<string>(x => x.Request.HttpMethod).Returns("OPTIONS").Verifiable();
            mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
            mock.Setup(x => x.Response.Cache.SetExpires(It.IsAny<DateTime>())).Verifiable();
            mock.Setup(x => x.Response.AppendHeader("Allow", "GET, HEAD, OPTIONS")).Verifiable();
            mock.Setup(x => x.Response.Clear()).Verifiable();
            mock.SetupSet(x => x.Response.StatusCode).Callback(actual => Assert.Equal<int>(actual, (int)HttpStatusCode.OK)).Verifiable();
            mock.Setup(x => x.Response.End()).Verifiable();

            AllowAttribute obj = new AllowAttribute
            {
                Methods = "GET, HEAD, OPTIONS"
            };

            obj.OnActionExecuting(new ActionExecutingContext { HttpContext = mock.Object });

            mock.VerifyAll();
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContext_whenMethodsNull()
        {
            var mock = new Mock<HttpContextBase>();
            mock.SetupGet<string>(x => x.Request.HttpMethod).Returns("OPTIONS").Verifiable();
            mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
            mock.Setup(x => x.Response.Cache.SetExpires(It.IsAny<DateTime>())).Verifiable();
            mock.Setup(x => x.Response.AppendHeader("Allow", "GET, HEAD, OPTIONS")).Verifiable();
            mock.Setup(x => x.Response.Clear()).Verifiable();
            mock.SetupSet(x => x.Response.StatusCode).Callback(actual => Assert.Equal<int>(actual, (int)HttpStatusCode.OK)).Verifiable();
            mock.Setup(x => x.Response.End()).Verifiable();

            new AllowAttribute().OnActionExecuting(new ActionExecutingContext() { HttpContext = mock.Object });

            mock.VerifyAll();
        }

        [Fact]
        public void op_OnActionExecuting_ActionExecutingContext_whenRequestOptions()
        {
            var mock = new Mock<HttpContextBase>();
            mock.SetupGet<string>(x => x.Request.HttpMethod).Returns("OPTIONS").Verifiable();
            mock.Setup(x => x.Response.Cache.SetCacheability(HttpCacheability.Public)).Verifiable();
            mock.Setup(x => x.Response.Cache.SetExpires(It.IsAny<DateTime>())).Verifiable();
            mock.Setup(x => x.Response.AppendHeader("Allow", "GET, HEAD, OPTIONS")).Verifiable();
            mock.Setup(x => x.Response.Clear()).Verifiable();
            mock.SetupSet(x => x.Response.StatusCode).Callback(actual => Assert.Equal<int>(actual, (int)HttpStatusCode.OK)).Verifiable();
            mock.Setup(x => x.Response.End()).Verifiable();

            new AllowAttribute().OnActionExecuting(new ActionExecutingContext() { HttpContext = mock.Object });

            mock.VerifyAll();
        }
    }
}